package solver.moves;

import java.util.List;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import model.EnhancedCostEvaluator;
import model.MachineSelector;
import model.Solution;
import model.SolverEnvironment;
import solver.BaseExtraLeanIPModelBuilder;
import solver.GrbExtraLeanIPModelBuilder;
import solver.SelectionAgent4;

public class MathMoveNeighborhoodXLocations extends AbstractComplexMove {

	protected static Logger log = LoggerFactory
			.getLogger(AbstractComplexMove.class);
	public MachineSelector ms;

	public MathMoveNeighborhoodXLocations(SolverEnvironment env,MachineSelector ms) {
		super(env);
		this.ms = ms;
	}

	private static int init_time = 10;
	private int time_sec = init_time;

	private int selectedNeighborhood;

	public int getSelectedNeighborhood() {
		return selectedNeighborhood;
	}

	public void setSelectedNeighborhood(int selectedNeighborhood) {
		this.selectedNeighborhood = selectedNeighborhood;
	}

	@Override
	public boolean performMove() {
		if (isMathSolverBeenUsed)
			return false;
		isMathSolverBeenUsed = true;
//		env.getCostCalculator().printNewSolutionCostAnalysis(
//				"BEFORE CALL OF MATH SOLVER");
		long initial_cost = env.getCostCalculator().getNewSolutionCost();
		SelectionAgent4 sa4 = new SelectionAgent4(env);
		sa4.setDependencyConstraintsNeeded(false);
		int n = selectedNeighborhood;
//		int l=-1;
//		for(int _m=0;_m<env.getProblem().M;_m++){
////		for(int _m=env.getProblem().M-1;_m>=0;_m--){
//			int m = ms.getMostLoadedMachine(_m);
//			if(env.getProblem().getNeighborhoodIdForMachineId(m)==n){
//				l=env.getProblem().getLocationIdForMachineId(m);
//				break;
//			}
//		}
			
		
		boolean success = false;
		int l_start = env.getRandom().nextInt(env.getProblem().L);
		int l_end = Math.min(l_start + 5, env.getProblem().L);

		for (int _l = l_start; _l < l_end; _l++) {
			int l = _l % env.getProblem().L;
			List<Integer> selectedMachines = env.getProblem().neighborhoodsXlocations[n][l];
			if (selectedMachines.isEmpty()){
//				System.err.println("Math move neighborhood: no selected machines");
//				isMathSolverBeenUsed = false;
//				return false;
			
				continue;
			}
			int process_limit = 40+env.getRandom().nextInt(20);
			List<Integer> selectedProcesses = sa4
					.selectAllProcessesForMachines(selectedMachines,
							process_limit);
			sa4.setLimitXvars(500);
			// List<Integer> selectedProcesses = sa4
			// .selectAllProcessesForMachines(selectedMachines, -1); // all
			// processes
			if (selectedProcesses.isEmpty()){
//				System.err.println("Math move neighborhood: no selected machines");
//				isMathSolverBeenUsed = false;
//				return false;
			
				continue;
			}
			log.debug("\n"
					+ getProblem().getSelectedProcessesAndEligibleMachines(
							selectedProcesses, true, env));
			BaseExtraLeanIPModelBuilder modelBuilder = new GrbExtraLeanIPModelBuilder(
					getProblem(), selectedProcesses, false, time_sec, getCc()
							.getNewSolutionCost());
			
			modelBuilder.setInitialSolution(env.getInitialSolution());
			modelBuilder.setCurrentSolution(env.getCurrentSolution().copy());
			modelBuilder.includeDependencyHardConstraints(false);
			modelBuilder.includeSpreadHardConstraints(false);
			
			//Ignore minor costs
//			 modelBuilder.includeServiceMoveCostSoftConstraints(false);
//			 modelBuilder.includeMachineMovedCostSoftConstraints(false);
//			 modelBuilder.includeProcessMoveCostSoftConstraints(false);
			boolean solved = modelBuilder.solve(true);
			if (solved
//					&& ((GrbExtraLeanIPModelBuilder) modelBuilder)
//							.computeAndGetCostOverIPModel() < env
//							.getCostCalculator().getNewSolutionCost()
							) {
				time_sec = init_time;
				long old_cost = env.getCostCalculator().getNewSolutionCost();
				System.out.println("MATH MOVE NEIGHBORHOOD PROBLEM SOLVED");
				// Solution sol = modelBuilder.saveToSolution();
				modelBuilder.updateSolution(env, selectedProcesses);
				if (env.getCostCalculator().getNewSolutionCost() >= old_cost) {
					getInvoker().undoAllCommands();
					env.getCostCalculator().clearDelta();
					getCc().setAndRestoreCostComponentMemory(previousCostState);
				} else {
					getInvoker().clearCommands();
					env.getCostCalculator().clearDelta();
					previousCostState = getCc().saveAndGetCostComponentMemory();
					success = true;
				}
				
				// break;
			} else {
				time_sec = Math.min(time_sec + 3, 3 * init_time);
//				System.err.println("MATH MOVE NEIGHBORHOOD PROBLEM NOT SOLVED. New time="+time_sec);
//				log.debug(String
//						.format("MATH PROBLEM NOT SOLVED OR NOT BETTER SOLUTION FOUND [SOLVED=%s IP_COST=%,d CC_COST=%,d]",
//								solved,
//								((GrbExtraLeanIPModelBuilder) modelBuilder)
//										.computeAndGetCostOverIPModel(), env
//										.getCostCalculator()
//										.getNewSolutionCost()));

			}
		}
//		env.getCostCalculator().printNewSolutionCostAnalysis(
//				"AFTER CALL OF MATH SOLVER");
		// env.getFullValidator().computeCost();
		// long after_call_cost = env.getFullValidator().getNewSolutionCost();
		long after_call_cost = env.getCostCalculator().getNewSolutionCost();
		log.info(String.format("Cost reduced by %,d", initial_cost
				- after_call_cost));
		isMathSolverBeenUsed = false;
		return success;
	}

	public void undo() {

	}

}
